package com.cby.android.olive.utils

import com.cby.android.olive.ssl.HttpsHostnameVerifier
import com.cby.android.olive.ssl.HttpsSSLSocketFactory
import org.json.JSONObject
import java.io.*
import java.net.*
import java.util.concurrent.TimeoutException
import javax.net.ssl.HttpsURLConnection
import javax.net.ssl.SSLException
import javax.net.ssl.SSLHandshakeException

/**
 * @description: HttpUtils--网络请求工具类
 *
 * @author ChenBaoYang
 * @date 6/1/21 11:26
 */
object HttpUtils {

    private const val REQ_PROPERTY_CONNECTION = "Connection"
    private const val REQ_VALUE_CONNECTION = "Keep-Alive"

    /**
     * http请求方法 - GET
     */
    private const val HTTP_METHOD_GET = "GET"

    /**
     * http请求方法 - POST
     */
    private const val HTTP_METHOD_POST = "POST"

    /**
     * utf-8字符集
     */
    private const val CHARSET_UTF8 = "UTF-8"

    /**
     * 连接超时时间，7秒
     */
    private const val CONNECT_TIMEOUT = 7 * 1000

    /**
     * 读取超时时间
     */
    private const val READ_TIMEOUT = 7 * 1000

    /**
     * get网络请求
     * @param baseUrl
     * @param params
     * @param listener [OnHttpRequestListener]
     */
    @JvmStatic
    fun requestGet(
        baseUrl: String,
        params: Map<String, Any>?,
        listener: OnHttpRequestListener?
    ) {
        var baseUrl = baseUrl
        var urlConnection: HttpURLConnection? = null
        try {
            val paramStr = buildParams(params)
            if (paramStr !== "") {
                baseUrl = "$baseUrl?$paramStr"
            }
            urlConnection = openURLConnection(baseUrl)
            urlConnection.requestMethod = HTTP_METHOD_GET
            urlConnection.connect()
            parseResponse(urlConnection, listener)
        } catch (e: Exception) {
            listener?.onResponseError(-4000, e.message)
        } finally {
            closeConnection(urlConnection)
        }
    }

    @JvmStatic
    fun requestPost(
        url: String,
        params: Map<String, Any>?,
        listener: OnHttpRequestListener?
    ) {
        requestPost(url, params, null, listener)
    }

    /**
     * POST参数
     * @param baseUrl
     * @param params
     * @param headers 用户信息串，
     * @param listener
     */
    @JvmStatic
    fun requestPost(
        baseUrl: String,
        params: Map<String, Any>?,
        headers: Map<String?, String?>?,
        listener: OnHttpRequestListener?
    ) {
        var urlConnection: HttpURLConnection? = null
        try {
            urlConnection = openURLConnection(baseUrl)
            if (headers != null && headers.isNotEmpty()) {
                for (key in headers.keys) {
                    urlConnection.setRequestProperty(key, headers[key])
                }
            }
            urlConnection.requestMethod = HTTP_METHOD_POST
            /**
             * 设置输入
             */
            urlConnection.doInput = true
            /**
             * 设置输出
             */
            urlConnection.doOutput = true
            val newParams = buildParams(params)

            //参数写入
            val outputStream = DataOutputStream(
                urlConnection.outputStream
            )
            outputStream.write(newParams.toByteArray())
            outputStream.flush()
            outputStream.close()

            //返回值解析
            parseResponse(urlConnection, listener)
        } catch (e: Exception) {
            if (listener != null) {
                if (e is UnknownHostException ||
                    e is ConnectException ||
                    e is TimeoutException ||
                    e is ProtocolException ||
                    e is IOException ||
                    e is EOFException ||
                    e is SSLException ||
                    e is SSLHandshakeException
                ) {
                    listener.onResponseError(-4001, "网络连接异常")
                } else {
                    listener.onResponseError(-4000, e.message)
                }
            }
        } finally {
            closeConnection(urlConnection)
        }
    }

    /**
     * POST参数
     * @param baseUrl
     * @param jsonParam
     * @param listener
     */
    @JvmStatic
    fun requestPost(
        baseUrl: String,
        jsonParam: JSONObject,
        listener: OnHttpRequestListener?
    ) {
        var urlConnection: HttpURLConnection? = null
        try {
            urlConnection = openURLConnection(baseUrl)
            urlConnection.requestMethod = HTTP_METHOD_POST
            /**
             * 设置输入
             */
            urlConnection.doInput = true
            /**
             * 设置输出
             */
            urlConnection.doOutput = true
            /**
             * 参数写入
             */
            val printWriter = PrintWriter(urlConnection.outputStream)
            printWriter.write(jsonParam.toString())
            printWriter.flush()

            //返回值解析
            parseResponse(urlConnection, listener)
            printWriter.close()
        } catch (e: Exception) {
            if (listener != null) {
                if (e is UnknownHostException ||
                    e is ConnectException ||
                    e is TimeoutException ||
                    e is ProtocolException ||
                    e is IOException ||
                    e is EOFException ||
                    e is SSLException ||
                    e is SSLHandshakeException
                ) {
                    listener.onResponseError(-4001, "请检测网络")
                } else {
                    listener.onResponseError(-4000, e.message)
                }
            }
        } finally {
            closeConnection(urlConnection)
        }
    }

    /**
     * 解析response信息
     * @param urlConnection
     * @param listener
     */
    @Throws(Exception::class)
    private fun parseResponse(
        urlConnection: HttpURLConnection?,
        listener: OnHttpRequestListener?
    ) {
        val httpStatus = urlConnection!!.responseCode
        if (isOK(httpStatus)) {
            val result = streamToString(urlConnection.inputStream)
            listener?.onResponseSuccess(result)
        } else {
            listener?.onResponseError(httpStatus, urlConnection.responseMessage)
        }
    }

    /**
     * 网络请求是否成功
     */
    private fun isOK(responseCode: Int): Boolean {
        return responseCode == HttpURLConnection.HTTP_OK
    }

    /**
     * 参数构建
     * @param params
     * @return
     */
    private fun buildParams(params: Map<String, Any>?): String {
        if (null == params || params.isEmpty()) {
            return ""
        }
        val paramSB = StringBuilder()
        var isFirst = true
        for (key in params.keys) {
            if (isFirst) {
                isFirst = false
            } else {
                paramSB.append("&")
            }
            paramSB.append(key).append("=").append(URLEncoder.encode(params[key].toString()))
        }
        return paramSB.toString()
    }

    /**
     * 关闭连接
     * @param connection
     */
    private fun closeConnection(connection: HttpURLConnection?) {
        connection?.disconnect()
    }

    /**
     * 打开一个连接
     * @param baseUrl
     * @return
     * @throws IOException
     */
    @Throws(IOException::class)
    private fun openURLConnection(baseUrl: String): HttpURLConnection {
        val url = URL(baseUrl)
        var urlConnection = url.openConnection() as HttpURLConnection
        //添加https的认证
        if (baseUrl.toUpperCase().startsWith("HTTPS")) {
            val httpsURLConnection = urlConnection as HttpsURLConnection
            httpsURLConnection.hostnameVerifier = HttpsHostnameVerifier()
            httpsURLConnection.sslSocketFactory = HttpsSSLSocketFactory.factory()
            urlConnection = httpsURLConnection
        }
        // Workaround for the M release HttpURLConnection not observing the
        // HttpURLConnection.setFollowRedirects() property.
        // https://code.google.com/p/android/issues/detail?id=194495
        urlConnection.instanceFollowRedirects = HttpURLConnection.getFollowRedirects()
        //urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");

        //设置超时时间
        urlConnection.connectTimeout = CONNECT_TIMEOUT
        urlConnection.readTimeout = READ_TIMEOUT

        //设置连接类型
        urlConnection.setRequestProperty(
            REQ_PROPERTY_CONNECTION,
            REQ_VALUE_CONNECTION
        )

        //不使用缓存
        urlConnection.useCaches = false
        return urlConnection
    }

    /**
     * 流转换为string字符串
     * @param inputStream
     * @return
     * @throws IOException
     */
    @Throws(IOException::class)
    private fun streamToString(inputStream: InputStream): String {
        val br = BufferedReader(
            InputStreamReader(
                inputStream,
                CHARSET_UTF8
            )
        )
        val sb = StringBuffer()
        var line: String? = null
        while (br.readLine().also { line = it } != null) {
            sb.append(line)
        }
        br.close()
        return sb.toString()
    }

    /**
     * Http网络请求监听
     */
    interface OnHttpRequestListener {
        /**
         * 网络请求成功
         * @param response
         */
        @Throws(Exception::class)
        fun onResponseSuccess(response: String?)

        /**
         * 网络请求失败
         * @param code
         * @param errorMessage
         */
        fun onResponseError(code: Int, errorMessage: String?)
    }
}